//
// This code was created by Jeff Molofee '99 (ported to Linux/GLUT by Richard Campbell '99)
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe 
// (email Richard Campbell at ulmont@bellsouth.net)
//
#include <GL/glut.h>    // Header File For The GLUT Library 
#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#include <unistd.h>     // Header file for sleeping.
#include <stdio.h>
#include <string.h>	// 字符串处理函数
#include <math.h>
#include <sys/time.h>

/* ascii code for the escape key */
#define ESCAPE 27

/* The number of our GLUT window */
int window; 

typedef unsigned short int	bool;
#define true	1
#define	false	0

#define __ARB_ENABLE	true
#define EXT_INFO
#define MAX_EXTENSION_SPACE		10240
#define MAX_EXTENSION_LENGTH	256
GLint	maxTexelUnits=1;		// Number Of Texel-Pipelines. This Is At Least 1.
bool	multitextureSupported	= false;
bool	useMultitexture			= true;

GLint	maxTexeUnits			= 1;
bool	emboss					= false;
bool	bumps					= true;
bool	wireframe				= false;

#define MAX_EMBOSS	(GLfloat)0.01f

GLfloat     xrot;
GLfloat     yrot;
GLfloat		xspeed;
GLfloat		yspeed;
GLfloat     z = -5.0f;

GLuint		filter = 1;
GLuint      texture[3];
GLuint		bump[3];
GLuint		invbump[3];
GLuint		glLogo;
GLuint		multiLogo;

#define	ONE_SECOND		1
#define	MAX_TEXTURES	10

/* creates a enum type for mouse buttons */
enum {
	BUTTON_LEFT = 0,
	BUTTON_RIGHT,
	BUTTON_LEFT_TRANSLATE,
};

/* set global variables */
int mButton = -1;
int mOldY, mOldX;

/* vectors that makes the rotation and translation of the cube */
float eye[3] = {0.0f, 0.0f, 0.0f};
float rot[3] = {0.0f, 0.0f, 0.0f};

GLfloat	LightAmbient[] = { 0.2f, 0.2f, 0.2f };
GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f };
GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f };

GLfloat Gray[] = { 0.5f, 0.5f, 0.5f, 1.0f };

GLfloat data[] = {
	// FRONT FACE
	0.0f, 0.0f,		-1.0f, -1.0f, +1.0f,
	1.0f, 0.0f,		+1.0f, -1.0f, +1.0f,
	1.0f, 1.0f,		+1.0f, +1.0f, +1.0f,
	0.0f, 1.0f,		-1.0f, +1.0f, +1.0f,
	// BACK FACE
	1.0f, 0.0f,		-1.0f, -1.0f, -1.0f,
	1.0f, 1.0f,		-1.0f, +1.0f, -1.0f,
	0.0f, 1.0f,		+1.0f, +1.0f, -1.0f,
	0.0f, 0.0f,		+1.0f, -1.0f, -1.0f,
	// Top Face
	0.0f, 1.0f,		-1.0f, +1.0f, -1.0f,
	0.0f, 0.0f,		-1.0f, +1.0f, +1.0f,
	1.0f, 0.0f,		+1.0f, +1.0f, +1.0f,
	1.0f, 1.0f,		+1.0f, +1.0f, -1.0f,
	// Bottom Face
	1.0f, 1.0f,		-1.0f, -1.0f, -1.0f,
	0.0f, 1.0f,		+1.0f, -1.0f, -1.0f,
	0.0f, 0.0f,		+1.0f, -1.0f, +1.0f,
	1.0f, 0.0f,		-1.0f, -1.0f, +1.0f,
	// Right Face
	1.0f, 0.0f,		+1.0f, -1.0f, -1.0f,
	1.0f, 1.0f,		+1.0f, +1.0f, -1.0f,
	0.0f, 1.0f,		+1.0f, +1.0f, +1.0f,
	0.0f, 0.0f,		+1.0f, -1.0f, +1.0f,
	// Left Face
	0.0f, 0.0f,		-1.0f, -1.0f, -1.0f,
	1.0f, 0.0f,		-1.0f, -1.0f,  1.0f,
	1.0f, 1.0f,		-1.0f,  1.0f,  1.0f,
	0.0f, 1.0f,		-1.0f,  1.0f, -1.0f
};

bool isInString(char* string, const char* search)
{
	int pos = 0;
	int maxpos = strlen(search) -1;
	int len = strlen(string);
	int i;
	for (i = 0; i < len; ++i)
	{
		if ((i == 0) || ((i > 1) && string[i-1] == '\n'))
		{
			pos = 0;
			while (string[i] != '\n')
			{
				if (string[i] == search[pos])
					pos++;
				if ((pos > maxpos) && string[i+1]== '\n')
					return true;
				i++;
			}
		}
	}
	return false;
}

bool initMultitexture(void)
{
	char* extensions;
	extensions = strdup((char*) glGetString(GL_EXTENSIONS)); //取得扩展字符串
	int len = strlen(extensions);
	int i;
	for (i = 0; i < len; ++i)
		if (extensions[i] == ' ')
			extensions[i] = '\n';
#ifdef EXT_INFO
	printf("%s", "\nSupported GL extensions");
#endif
	if (isInString(extensions, "GL_ARB_multitexture")
			&& __ARB_ENABLE
			&& isInString(extensions, "GL_EXT_texture_env_combine"))
	{
#ifdef EXT_INFO
		printf("%s", "\nThe GL_ARB_multitexture extension will be used.\n");
#endif
		return true;
	}
	useMultitexture = false;
	return false;
}

void initLights(void)
{
	glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient);
	glLightfv( GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
	glLightfv( GL_LIGHT1, GL_PROJECTION, LightPosition);
	glEnable(GL_LIGHT1);
}
////////////////////////////////////////// \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
//
// These defines are used to tell us about the type of TARGA file it is
// 
////////////////////////////////////////// \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

#define TGA_RGB		 2		// This tells us it's a normal RGB (really BGR) file
#define TGA_A		 3		// This tells us it's a ALPHA file
#define TGA_RLE		10		// This tells us that the targa is Run-Length Encoded (RLE)

// This is our image structure for our targa data
typedef struct tImageTGA
{
	int channels;			// The channels in the image (3 = RGB : 4 = RGBA)
	int sizeX;			// The width of the image in pixels
	int sizeY;			// The height of the image in pixels
	unsigned char *data;		// The image pixel data
}ImageTGA;


typedef unsigned char  byte;
typedef unsigned short WORD;
typedef unsigned int   UINT;

///////////////////////////////// LOAD TGA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This loads the TGA file and returns it's data in a ImageTGA struct
/////
///////////////////////////////// LOAD TGA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

ImageTGA *LoadTGA(const char *filename)
{
	ImageTGA *pImageData = NULL;		// This stores our important image data
	WORD width = 0, height = 0;			// The dimensions of the image
	byte length = 0;					// The length in bytes to the pixels
	byte imageType = 0;					// The image type (RLE, RGB, Alpha...)
	byte bits = 0;						// The bits per pixel for the image (16, 24, 32)
	FILE *pFile = NULL;					// The file pointer
	int channels = 0;					// The channels of the image (3 = RGA : 4 = RGBA)
	int stride = 0;						// The stride (channels * width)
	int i = 0;							// A counter

	// This function loads in a TARGA (.TGA) file and returns its data to be
	// used as a texture or what have you.  This currently loads in a 16, 24
	// and 32-bit targa file, along with RLE compressed files.  Eventually you
	// will want to do more error checking to make it more robust.  This is
	// also a perfect start to go into a modular class for an engine.
	// Basically, how it works is, you read in the header information, then
	// move your file pointer to the pixel data.  Before reading in the pixel
	// data, we check to see the if it's an RLE compressed image.  This is because
	// we will handle it different.  If it isn't compressed, then we need another
	// check to see if we need to convert it from 16-bit to 24 bit.  24-bit and
	// 32-bit textures are very similar, so there's no need to do anything special.
	// We do, however, read in an extra bit for each color.

	// Open a file pointer to the targa file and check if it was found and opened 
	fprintf(stdout, "filename: %s\n", filename);
	if((pFile = fopen(filename, "rb")) == NULL) 
	{
		// Display an error message saying the file was not found, then return NULL
		printf("\nUnable to load TGA File!\n");
		return NULL;
	}

	// Allocate the structure that will hold our eventual image data (must free it!)
	pImageData = (ImageTGA*)malloc(sizeof(ImageTGA));

	// Read in the length in bytes from the header to the pixel data
	fread(&length, sizeof(byte), 1, pFile);

	// Jump over one byte
	fseek(pFile,1,SEEK_CUR); 

	// Read in the imageType (RLE, RGB, etc...)
	fread(&imageType, sizeof(byte), 1, pFile);

	// Skip past general information we don't care about
	fseek(pFile, 9, SEEK_CUR); 

	// Read the width, height and bits per pixel (16, 24 or 32)
	fread(&width,  sizeof(WORD), 1, pFile);
	fread(&height, sizeof(WORD), 1, pFile);
	fread(&bits,   sizeof(byte), 1, pFile);

	// Now we move the file pointer to the pixel data
	fseek(pFile, length + 1, SEEK_CUR); 

	// Check if the image is RLE compressed or not
	if(imageType != TGA_RLE)
	{
		// Check if the image is a 24 or 32-bit image
		if(bits == 24 || bits == 32)
		{
			// Calculate the channels (3 or 4) - (use bits >> 3 for more speed).
			// Next, we calculate the stride and allocate enough memory for the pixels.
			channels = bits / 8;
			stride = channels * width;
			pImageData->data = (unsigned char*)malloc(stride * height * sizeof(unsigned char));

			int y;
			// Load in all the pixel data line by line
			for(y = 0; y < height; y++)
			{
				// Store a pointer to the current line of pixels
				unsigned char *pLine = &(pImageData->data[stride * y]);

				// Read in the current line of pixels
				fread(pLine, stride, 1, pFile);

				// Go through all of the pixels and swap the B and R values since TGA
				// files are stored as BGR instead of RGB (or use GL_BGR_EXT verses GL_RGB)
				for(i = 0; i < stride; i += channels)
				{
					int temp     = pLine[i];
					pLine[i]     = pLine[i + 2];
					pLine[i + 2] = temp;
				}
			}
		}
		// Check if the image is a 16 bit image (RGB stored in 1 unsigned short)
		else if(bits == 16)
		{
			unsigned short pixels = 0;
			int r=0, g=0, b=0;

			// Since we convert 16-bit images to 24 bit, we hardcode the channels to 3.
			// We then calculate the stride and allocate memory for the pixels.
			channels = 3;
			stride = channels * width;
			pImageData->data = (unsigned char*)malloc(stride * height * sizeof(unsigned char));

			// Load in all the pixel data pixel by pixel
			for(i = 0; i < width*height; i++)
			{
				// Read in the current pixel
				fread(&pixels, sizeof(unsigned short), 1, pFile);

				// To convert a 16-bit pixel into an R, G, B, we need to
				// do some masking and such to isolate each color value.
				// 0x1f = 11111 in binary, so since 5 bits are reserved in
				// each unsigned short for the R, G and B, we bit shift and mask
				// to find each value.  We then bit shift up by 3 to get the full color.
				b = (pixels & 0x1f) << 3;
				g = ((pixels >> 5) & 0x1f) << 3;
				r = ((pixels >> 10) & 0x1f) << 3;

				// This essentially assigns the color to our array and swaps the
				// B and R values at the same time.
				pImageData->data[i * 3 + 0] = r;
				pImageData->data[i * 3 + 1] = g;
				pImageData->data[i * 3 + 2] = b;
			}
		}	
		// Else return a NULL for a bad or unsupported pixel format
		else
			return NULL;
	}
	// Else, it must be Run-Length Encoded (RLE)
	else
	{
		// First, let me explain real quickly what RLE is.  
		// For further information, check out Paul Bourke's intro article at: 
		// http://astronomy.swin.edu.au/~pbourke/dataformats/rle/
		// 
		// Anyway, we know that RLE is a basic type compression.  It takes
		// colors that are next to each other and then shrinks that info down
		// into the color and a integer that tells how much of that color is used.
		// For instance:
		// aaaaabbcccccccc would turn into a5b2c8
		// Well, that's fine and dandy and all, but how is it down with RGB colors?
		// Simple, you read in an color count (rleID), and if that number is less than 128,
		// it does NOT have any optimization for those colors, so we just read the next
		// pixels normally.  Say, the color count was 28, we read in 28 colors like normal.
		// If the color count is over 128, that means that the next color is optimized and
		// we want to read in the same pixel color for a count of (colorCount - 127).
		// It's 127 because we add 1 to the color count, as you'll notice in the code.

		// Create some variables to hold the rleID, current colors read, channels, & stride.
		byte rleID = 0;
		int colorsRead = 0;
		channels = bits / 8;
		stride = channels * width;

		// Next we want to allocate the memory for the pixels and create an array,
		// depending on the channel count, to read in for each pixel.
		pImageData->data = (unsigned char*)malloc(stride * height * sizeof(unsigned char));
		char *pColors = (char*)malloc(channels);

		// Load in all the pixel data
		while(i < width*height)
		{
			// Read in the current color count + 1
			fread(&rleID, sizeof(byte), 1, pFile);

			// Check if we don't have an encoded string of colors
			if(rleID < 128)
			{
				// Increase the count by 1
				rleID++;

				// Go through and read all the unique colors found
				while(rleID)
				{
					// Read in the current color
					fread(pColors, sizeof(byte) * channels, 1, pFile);

					// Store the current pixel in our image array
					pImageData->data[colorsRead + 0] = pColors[2];
					pImageData->data[colorsRead + 1] = pColors[1];
					pImageData->data[colorsRead + 2] = pColors[0];

					// If we have a 4 channel 32-bit image, assign one more for the alpha
					if(bits == 32)
						pImageData->data[colorsRead + 3] = pColors[3];

					// Increase the current pixels read, decrease the amount
					// of pixels left, and increase the starting index for the next pixel.
					i++;
					rleID--;
					colorsRead += channels;
				}
			}
			// Else, let's read in a string of the same character
			else
			{
				// Minus the 128 ID + 1 (127) to get the color count that needs to be read
				rleID -= 127;

				// Read in the current color, which is the same for a while
				fread(pColors, sizeof(byte) * channels, 1, pFile);

				// Go and read as many pixels as are the same
				while(rleID)
				{
					// Assign the current pixel to the current index in our pixel array
					pImageData->data[colorsRead + 0] = pColors[2];
					pImageData->data[colorsRead + 1] = pColors[1];
					pImageData->data[colorsRead + 2] = pColors[0];

					// If we have a 4 channel 32-bit image, assign one more for the alpha
					if(bits == 32)
						pImageData->data[colorsRead + 3] = pColors[3];

					// Increase the current pixels read, decrease the amount
					// of pixels left, and increase the starting index for the next pixel.
					i++;
					rleID--;
					colorsRead += channels;
				}

			}

		}
	}

	// Close the file pointer that opened the file
	fclose(pFile);

	// Fill in our ImageTGA structure to pass back
	pImageData->channels = channels;
	pImageData->sizeX    = width;
	pImageData->sizeY    = height;

	// Return the TGA data (remember, you must free this data after you are done)
	return pImageData;
}

///////////////////////////////// CREATE TEXTURE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This creates a texture in OpenGL that we can use as a texture map
/////
///////////////////////////////// CREATE TEXTURE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CreateTexture(GLuint textureArray[], char *strFileName, int textureID)
{
	if(!strFileName)									// Return from the function if no file name was passed in
		return;

	/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *	

	// Just like the other image loaders, we return a pointer to some image data,
	// which needs to be freed once we are finished with it, along with it's data.

	ImageTGA *pImage = LoadTGA(strFileName);			// Load the image and store the data

	/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *


	if(pImage == NULL)									// If we can't load the file, quit!
		exit(0);

	// Generate a texture with the associative texture ID stored in the array
	glGenTextures(1, &textureArray[textureID]);

	// Bind the texture to the texture arrays index and init the texture
	glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);


	/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *

	// Assume that the texture is a 24 bit RGB texture (We convert 16-bit ones to 24-bit)
	int textureType = GL_RGB;

	// If the image is 32-bit (4 channels), then we need to specify GL_RGBA for an alpha
	if(pImage->channels == 4)
		textureType = GL_RGBA;

	// Build Mipmaps (builds different versions of the picture for distances - looks better)
	gluBuild2DMipmaps(GL_TEXTURE_2D, pImage->channels, pImage->sizeX, 
			pImage->sizeY, textureType, GL_UNSIGNED_BYTE, pImage->data);

	/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *


	// Lastly, we need to tell OpenGL the quality of our texture map.  GL_LINEAR_MIPMAP_LINEAR
	// is the smoothest.  GL_LINEAR_MIPMAP_NEAREST is faster than GL_LINEAR_MIPMAP_LINEAR, 
	// but looks blochy and pixilated.  Good for slower computers though. 

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);	

	// Now we need to free the image data that we loaded since OpenGL stored it as a texture

	if (pImage)										// If we loaded the image
	{
		if (pImage->data)							// If there is texture data
		{
			free(pImage->data);						// Free the texture data, we don't need it anymore
		}

		free(pImage);								// Free the image structure
	}
}

// Here I swapped auxDIBImageLoad for LoadTGA
int LoadGLTextures(){												// Load Bitmaps And Convert To Textures
	bool status=true;												// Status Indicator
	ImageTGA *Image=NULL;									// Create Storage Space For The Texture
	char *alpha=NULL;
	int a, i;

	// Load The Tile-Bitmap For Base-Texture
	if ((Image=LoadTGA("Data/lesson22/Base.tga")) != NULL) {											
		glGenTextures(3, texture);									// Create Three Textures

		// Create Nearest Filtered Texture
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);
		//                             ========
		// Use GL_RGB8 Instead Of "3" In glTexImage2D. Also Defined By GL: GL_RGBA8 Etc.
		// NEW: Now Creating GL_RGBA8 Textures, Alpha Is 1.0f Where Not Specified By Format.

		// Create Linear Filtered Texture
		glBindTexture(GL_TEXTURE_2D, texture[1]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		// Create MipMapped Texture
		glBindTexture(GL_TEXTURE_2D, texture[2]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, Image->sizeX, Image->sizeY, GL_RGB, GL_UNSIGNED_BYTE, Image->data);
	}
	else status=false;
	if (Image) {													// If Texture Exists
		if (Image->data) free(Image->data);						// If Texture Image Exists
		free(Image);
		Image=NULL;
	}	

	// Load The Bumpmaps
	if ((Image=LoadTGA("Data/lesson22/Bump.tga")) != NULL) {			
		glPixelTransferf(GL_RED_SCALE,0.5f);						// Scale RGB By 50%, So That We Have Only			
		glPixelTransferf(GL_GREEN_SCALE,0.5f);						// Half Intenstity
		glPixelTransferf(GL_BLUE_SCALE,0.5f);

		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);	// No Wrapping, Please!
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
		glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,Gray);

		glGenTextures(3, bump);										// Create Three Textures

		// Create Nearest Filtered Texture
		glBindTexture(GL_TEXTURE_2D, bump[0]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		// Create Linear Filtered Texture
		glBindTexture(GL_TEXTURE_2D, bump[1]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		// Create MipMapped Texture
		glBindTexture(GL_TEXTURE_2D, bump[2]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, Image->sizeX, Image->sizeY, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		for (i=0; i<3*Image->sizeX*Image->sizeY; i++)		// Invert The Bumpmap
			Image->data[i]=255-Image->data[i];

		glGenTextures(3, invbump);								// Create Three Textures

		// Create Nearest Filtered Texture
		glBindTexture(GL_TEXTURE_2D, invbump[0]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		// Create Linear Filtered Texture
		glBindTexture(GL_TEXTURE_2D, invbump[1]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		// Create MipMapped Texture
		glBindTexture(GL_TEXTURE_2D, invbump[2]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, Image->sizeX, Image->sizeY, GL_RGB, GL_UNSIGNED_BYTE, Image->data);

		glPixelTransferf(GL_RED_SCALE,1.0f);				// Scale RGB Back To 100% Again		
		glPixelTransferf(GL_GREEN_SCALE,1.0f);			
		glPixelTransferf(GL_BLUE_SCALE,1.0f);
	}
	else status=false;
	if (Image) {											// If Texture Exists
		if (Image->data) free(Image->data);				// If Texture Image Exists
		free(Image);
	}

	// Load The Logo-Bitmaps
	if ((Image=LoadTGA("Data/lesson22/OpenGL_Alpha.tga")) != NULL) {							
		alpha=(char*)malloc(4*Image->sizeX*Image->sizeY);		// Create Memory For RGBA8-Texture
		for (a=0; a<Image->sizeX*Image->sizeY; a++)
			alpha[4*a+3]=Image->data[a*3];					// Pick Only Red Value As Alpha!
		if (!(Image=LoadTGA("Data/lesson22/OpenGL.tga"))) status=false;
		for (a=0; a<Image->sizeX*Image->sizeY; a++) {		
			alpha[4*a]=Image->data[a*3];					// R
			alpha[4*a+1]=Image->data[a*3+1];				// G
			alpha[4*a+2]=Image->data[a*3+2];				// B
		}

		glGenTextures(1, &glLogo);							// Create One Textures

		// Create Linear Filtered RGBA8-Texture
		glBindTexture(GL_TEXTURE_2D, glLogo);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Image->sizeX, Image->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, alpha);
		free(alpha);
	}
	else status=false;
	if (Image) {											// If Texture Exists
		if (Image->data) free(Image->data);				// If Texture Image Exists
		free(Image);
		Image=NULL;
	}	

	// Load The "Extension Enabled"-Logo
	if ((Image=LoadTGA("Data/lesson22/Multi_On_Alpha.tga")) != NULL) {							
		alpha=(char*)malloc(4*Image->sizeX*Image->sizeY);		// Create Memory For RGBA8-Texture
		for (a=0; a<Image->sizeX*Image->sizeY; a++)
			alpha[4*a+3]=Image->data[a*3];					// Pick Only Red Value As Alpha!
		if (!(Image=LoadTGA("Data/lesson22/Multi_On.tga"))) status=false;
		for (a=0; a<Image->sizeX*Image->sizeY; a++) {		
			alpha[4*a]=Image->data[a*3];					// R
			alpha[4*a+1]=Image->data[a*3+1];				// G
			alpha[4*a+2]=Image->data[a*3+2];				// B
		}

		glGenTextures(1, &multiLogo);						// Create One Textures

		// Create Linear Filtered RGBA8-Texture
		glBindTexture(GL_TEXTURE_2D, multiLogo);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Image->sizeX, Image->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, alpha);
		free(alpha);
	}
	else status=false;
	if (Image) {											// If Texture Exists
		if (Image->data) free(Image->data);				// If Texture Image Exists
		free(Image);
		Image=NULL;
	}		

	return status;											// Return The Status
}

// 计算向量v=v*M（左乘）
void VMatMult(GLfloat *M, GLfloat *v) {
	GLfloat res[3];
	res[0]=M[ 0]*v[0]+M[ 1]*v[1]+M[ 2]*v[2]+M[ 3]*v[3];
	res[1]=M[ 4]*v[0]+M[ 5]*v[1]+M[ 6]*v[2]+M[ 7]*v[3];
	res[2]=M[ 8]*v[0]+M[ 9]*v[1]+M[10]*v[2]+M[11]*v[3];
	v[0]=res[0];
	v[1]=res[1];
	v[2]=res[2];
	v[3]=M[15];								
}

void SetUpBumps(GLfloat *n, GLfloat *c, GLfloat *l, GLfloat *s, GLfloat *t) {
	GLfloat v[3];							// Vertex From Current Position To Light	
	GLfloat lenQ;							// Used To Normalize		

	// Calculate v From Current Vector c To Lightposition And Normalize v	
	v[0]=l[0]-c[0];		
	v[1]=l[1]-c[1];		
	v[2]=l[2]-c[2];		
	lenQ=(GLfloat) sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
	v[0]/=lenQ;		v[1]/=lenQ;		v[2]/=lenQ;
	// Project v Such That We Get Two Values Along Each Texture-Coordinat Axis.
	c[0]=(s[0]*v[0]+s[1]*v[1]+s[2]*v[2])*MAX_EMBOSS;
	c[1]=(t[0]*v[0]+t[1]*v[1]+t[2]*v[2])*MAX_EMBOSS;	
}


/* A general OpenGL initialization function.  Sets all of the initial parameters. */
void InitGL(int Width, int Height)	        // We call this right after our OpenGL window is created.
{
	multitextureSupported=initMultitexture();
	if (!LoadGLTextures())			// Jump To Texture Loading Routine
	{
		fprintf(stdout, "LoadGLTextures occurs error, exit.\n");
		exit(-1);
	}

	glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

	initLights();	
}

//------------------------------------------------------------------------
// Draws the current frame rate on screen
//------------------------------------------------------------------------

void drawFrameRate(char *str, void *font, GLclampf r, GLclampf g, GLclampf b,
		GLfloat x, GLfloat y) {
	/* font: font to use, e.g., GLUT_BITMAP_HELVETICA_10
	   r, g, b: text colour
	   x, y: text position in window: range [0,0] (bottom left of window)
	   to [1,1] (top right of window). */

	char *ch;
	GLint matrixMode;
	GLboolean lightingOn;

	lightingOn= glIsEnabled(GL_LIGHTING);        /* lighting on? */
	if (lightingOn) glDisable(GL_LIGHTING);

	glGetIntegerv(GL_MATRIX_MODE, &matrixMode);  /* matrix mode? */

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);;

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0.0, 1.0, 0.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glPushAttrib(GL_COLOR_BUFFER_BIT);       /* save current colour */
	glColor3f(r, g, b);
	glRasterPos3f(x, y, 0.0);
	for(ch= str; *ch; ch++) {
		glutBitmapCharacter(font, (int)*ch);
	}
	glPopAttrib();
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(matrixMode);

	if (lightingOn) glEnable(GL_LIGHTING);

	glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping
	glEnable(GL_DEPTH_TEST);
}

//------------------------------------------------------------------------
// Calculates the current frame rate
//------------------------------------------------------------------------

void CalculateFrameRate()
{


	static float framesPerSecond    = 0.0f;        // This will store our fps
	static long lastTime            = 0;           // This will hold the time from the last frame
	static char strFrameRate[50]    = {0};         // We will store the string here for the window title

	// struct for the time value
	struct timeval currentTime;
	currentTime.tv_sec  = 0;
	currentTime.tv_usec = 0; 

	// gets the microseconds passed since app started
	gettimeofday(&currentTime, NULL);	    

	// Increase the frame counter
	++framesPerSecond;

	if( currentTime.tv_sec - lastTime >= ONE_SECOND )
	{
		lastTime = currentTime.tv_sec;

		// Copy the frames per second into a string to display in the window
		sprintf(strFrameRate, "Current Frames Per Second: %d\n", (int)(framesPerSecond));

		// Reset the frames per second
		framesPerSecond = 0;

	}

	// draw frame rate on screen
	drawFrameRate(strFrameRate, GLUT_BITMAP_HELVETICA_10, 1.0f, 1.0f, 1.0f, 0.05, 0.95 );
	printf("\r%s", strFrameRate );
}



/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
void ReSizeGLScene(int Width, int Height)
{
	if (Height==0)				// Prevent A Divide By Zero If The Window Is Too Small
		Height=1;

	glViewport(0, 0, Width, Height);		// Reset The Current Viewport And Perspective Transformation

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
	glMatrixMode(GL_MODELVIEW);
}

void doCube (void) {
	int i;
	glBegin(GL_QUADS);
	// 前面
	glNormal3f( 0.0f, 0.0f, +1.0f);
	for (i=0; i<4; i++) {
		glTexCoord2f(data[5*i],data[5*i+1]);
		glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
	}
	// 后面
	glNormal3f( 0.0f, 0.0f,-1.0f);
	for (i=4; i<8; i++) {
		glTexCoord2f(data[5*i],data[5*i+1]);
		glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
	}
	// 上面
	glNormal3f( 0.0f, 1.0f, 0.0f);
	for (i=8; i<12; i++) {
		glTexCoord2f(data[5*i],data[5*i+1]);
		glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
	}
	// 下面
	glNormal3f( 0.0f,-1.0f, 0.0f);
	for (i=12; i<16; i++) {
		glTexCoord2f(data[5*i],data[5*i+1]);
		glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
	}
	// 右面
	glNormal3f( 1.0f, 0.0f, 0.0f);
	for (i=16; i<20; i++) {
		glTexCoord2f(data[5*i],data[5*i+1]);
		glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
	}
	// 左面
	glNormal3f(-1.0f, 0.0f, 0.0f);
	for (i=20; i<24; i++) {
		glTexCoord2f(data[5*i],data[5*i+1]);
		glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
	}
	glEnd();
}

void doLogo(void) {			// MUST CALL THIS LAST!!!, Billboards The Two Logos.
	glDepthFunc(GL_ALWAYS);		
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	glDisable(GL_LIGHTING);
	glLoadIdentity();	
	glBindTexture(GL_TEXTURE_2D,glLogo);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,0.0f);	glVertex3f(0.23f, -0.4f,-1.0f);
	glTexCoord2f(1.0f,0.0f);	glVertex3f(0.53f, -0.4f,-1.0f);
	glTexCoord2f(1.0f,1.0f);	glVertex3f(0.53f, -0.25f,-1.0f);
	glTexCoord2f(0.0f,1.0f);	glVertex3f(0.23f, -0.25f,-1.0f);
	glEnd();		
	if (useMultitexture) {
		glBindTexture(GL_TEXTURE_2D,multiLogo);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f,0.0f);	glVertex3f(-0.53f, -0.4f,-1.0f);
		glTexCoord2f(1.0f,0.0f);	glVertex3f(-0.33f, -0.4f,-1.0f);
		glTexCoord2f(1.0f,1.0f);	glVertex3f(-0.33f, -0.3f,-1.0f);
		glTexCoord2f(0.0f,1.0f);	glVertex3f(-0.53f, -0.3f,-1.0f);
		glEnd();		
	}
	glDepthFunc(GL_LEQUAL);
}

bool doMesh1TexelUnits(void) {

	GLfloat c[4]={0.0f,0.0f,0.0f,1.0f};					// Holds Current Vertex
	GLfloat n[4]={0.0f,0.0f,0.0f,1.0f};					// Normalized Normal Of Current Surface		
	GLfloat s[4]={0.0f,0.0f,0.0f,1.0f};					// s-Texture Coordinate Direction, Normalized
	GLfloat t[4]={0.0f,0.0f,0.0f,1.0f};					// t-Texture Coordinate Direction, Normalized
	GLfloat l[4];										// Holds Our Lightposition To Be Transformed Into Object Space
	GLfloat Minv[16];									// Holds The Inverted Modelview Matrix To Do So.
	int i;								

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer

	// Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
	// Simply Build It By Doing All Transformations Negated And In Reverse Order.
	glLoadIdentity();								
	glRotatef(-yrot,0.0f,1.0f,0.0f);
	glRotatef(-xrot,1.0f,0.0f,0.0f);
	glTranslatef(0.0f,0.0f,-z);
	glGetFloatv(GL_MODELVIEW_MATRIX,Minv);
	glLoadIdentity();
	glTranslatef(0.0f,0.0f,z);

	glRotatef(xrot,1.0f,0.0f,0.0f);
	glRotatef(yrot,0.0f,1.0f,0.0f);	

	// Transform The Lightposition Into Object Coordinates:
	l[0]=LightPosition[0];
	l[1]=LightPosition[1];
	l[2]=LightPosition[2];
	l[3]=1.0f;											// Homogenous Coordinate
	VMatMult(Minv,l);

	/*	PASS#1: Use Texture "Bump"
		No Blend
		No Lighting
		No Offset Texture-Coordinates */
	glBindTexture(GL_TEXTURE_2D, bump[filter]);
	glDisable(GL_BLEND);
	glDisable(GL_LIGHTING);
	doCube();

	/* PASS#2:	Use Texture "Invbump"
	   Blend GL_ONE To GL_ONE
	   No Lighting
	   Offset Texture Coordinates 
	   */
	glBindTexture(GL_TEXTURE_2D,invbump[filter]);
	glBlendFunc(GL_ONE,GL_ONE);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_BLEND);	

	glBegin(GL_QUADS);	
	// Front Face	
	n[0]=0.0f;		n[1]=0.0f;		n[2]=1.0f;			
	s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=0; i<4; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Back Face	
	n[0]=0.0f;		n[1]=0.0f;		n[2]=-1.0f;	
	s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=4; i<8; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Top Face	
	n[0]=0.0f;		n[1]=1.0f;		n[2]=0.0f;		
	s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
	for (i=8; i<12; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Bottom Face
	n[0]=0.0f;		n[1]=-1.0f;		n[2]=0.0f;		
	s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
	for (i=12; i<16; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Right Face	
	n[0]=1.0f;		n[1]=0.0f;		n[2]=0.0f;		
	s[0]=0.0f;		s[1]=0.0f;		s[2]=-1.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=16; i<20; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Left Face
	n[0]=-1.0f;		n[1]=0.0f;		n[2]=0.0f;		
	s[0]=0.0f;		s[1]=0.0f;		s[2]=1.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=20; i<24; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}		
	glEnd();

	/* PASS#3:	Use Texture "Base"
	   Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
	   Lighting Enabled
	   No Offset Texture-Coordinates
	   */
	if (!emboss) {
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glBindTexture(GL_TEXTURE_2D,texture[filter]);
		glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);	
		glEnable(GL_LIGHTING);
		doCube();
	}

	xrot+=xspeed;
	yrot+=yspeed;
	if (xrot>360.0f) xrot-=360.0f;
	if (xrot<0.0f) xrot+=360.0f;
	if (yrot>360.0f) yrot-=360.0f;
	if (yrot<0.0f) yrot+=360.0f;

	/*	LAST PASS:	Do The Logos! */
	doLogo();

	return true;										// Keep Going
}

bool doMesh2TexelUnits(void) {

	GLfloat c[4]={0.0f,0.0f,0.0f,1.0f};					// holds current vertex
	GLfloat n[4]={0.0f,0.0f,0.0f,1.0f};					// normalized normal of current surface		
	GLfloat s[4]={0.0f,0.0f,0.0f,1.0f};					// s-texture coordinate direction, normalized
	GLfloat t[4]={0.0f,0.0f,0.0f,1.0f};					// t-texture coordinate direction, normalized
	GLfloat l[4];										// holds our lightposition to be transformed into object space
	GLfloat Minv[16];									// holds the inverted modelview matrix to do so.
	int i;								

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer

	// Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
	// Simply Build It By Doing All Transformations Negated And In Reverse Order.
	glLoadIdentity();								
	glRotatef(-yrot,0.0f,1.0f,0.0f);
	glRotatef(-xrot,1.0f,0.0f,0.0f);
	glTranslatef(0.0f,0.0f,-z);
	glGetFloatv(GL_MODELVIEW_MATRIX,Minv);
	glLoadIdentity();
	glTranslatef(0.0f,0.0f,z);

	glRotatef(xrot,1.0f,0.0f,0.0f);
	glRotatef(yrot,0.0f,1.0f,0.0f);	

	// Transform The Lightposition Into Object Coordinates:
	l[0]=LightPosition[0];
	l[1]=LightPosition[1];
	l[2]=LightPosition[2];
	l[3]=1.0f;											// Homogenous Coordinate
	VMatMult(Minv,l);

	/*	PASS#1: Texel-Unit 0:	Use Texture "Bump"
		No Blend
		No Lighting
		No Offset Texture-Coordinates 
		Texture-Operation "Replace"
		Texel-Unit 1:	Use Texture "Invbump"
		No Lighting
		Offset Texture Coordinates 
		Texture-Operation "Replace"
		*/
	// TEXTURE-UNIT #0		
	glActiveTextureARB(GL_TEXTURE0_ARB);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, bump[filter]);
	glTexEnvf(GL_TEXTURE_ENV, (GLenum)GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
	glTexEnvf(GL_TEXTURE_ENV, (GLenum)GL_COMBINE_RGB_EXT, GL_REPLACE);	
	// TEXTURE-UNIT #1:
	glActiveTextureARB(GL_TEXTURE1_ARB);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, invbump[filter]);
	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
	glTexEnvf (GL_TEXTURE_ENV, (GLenum)GL_COMBINE_RGB_EXT, GL_ADD);
	// General Switches:
	glDisable(GL_BLEND);
	glDisable(GL_LIGHTING);	
	glBegin(GL_QUADS);	
	// Front Face	
	n[0]=0.0f;		n[1]=0.0f;		n[2]=1.0f;			
	s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=0; i<4; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]); 
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Back Face	
	n[0]=0.0f;		n[1]=0.0f;		n[2]=-1.0f;	
	s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=4; i<8; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]); 
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Top Face	
	n[0]=0.0f;		n[1]=1.0f;		n[2]=0.0f;		
	s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
	for (i=8; i<12; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     ); 
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Bottom Face
	n[0]=0.0f;		n[1]=-1.0f;		n[2]=0.0f;		
	s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
	t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
	for (i=12; i<16; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     ); 
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Right Face	
	n[0]=1.0f;		n[1]=0.0f;		n[2]=0.0f;		
	s[0]=0.0f;		s[1]=0.0f;		s[2]=-1.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=16; i<20; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     ); 
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}
	// Left Face
	n[0]=-1.0f;		n[1]=0.0f;		n[2]=0.0f;		
	s[0]=0.0f;		s[1]=0.0f;		s[2]=1.0f;
	t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
	for (i=20; i<24; i++) {	
		c[0]=data[5*i+2];		
		c[1]=data[5*i+3];
		c[2]=data[5*i+4];
		SetUpBumps(n,c,l,s,t);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     ); 
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); 
		glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
	}		
	glEnd();

	/* PASS#2	Use Texture "Base"
	   Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
	   Lighting Enabled
	   No Offset Texture-Coordinates
	   */	
	glActiveTextureARB(GL_TEXTURE1_ARB);		
	glDisable(GL_TEXTURE_2D);
	glActiveTextureARB(GL_TEXTURE0_ARB);			
	if (!emboss) {						
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glBindTexture(GL_TEXTURE_2D,texture[filter]);
		glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
		glEnable(GL_BLEND);
		glEnable(GL_LIGHTING);
		doCube();
	}

	xrot+=xspeed;
	yrot+=yspeed;
	if (xrot>360.0f) xrot-=360.0f;
	if (xrot<0.0f) xrot+=360.0f;
	if (yrot>360.0f) yrot-=360.0f;
	if (yrot<0.0f) yrot+=360.0f;

	/* LAST PASS:	Do The Logos! */	
	doLogo();

	return true;										// Keep Going
}
bool doMeshNoBumps(void) {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glLoadIdentity();									// Reset The View
	glTranslatef(0.0f,0.0f,z);

	glRotatef(xrot,1.0f,0.0f,0.0f);
	glRotatef(yrot,0.0f,1.0f,0.0f);	
	if (useMultitexture) {
		glActiveTextureARB(GL_TEXTURE1_ARB);		
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE0_ARB);
	}
	glDisable(GL_BLEND);
	glBindTexture(GL_TEXTURE_2D,texture[filter]);	
	glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
	glEnable(GL_LIGHTING);
	doCube();

	xrot+=xspeed;
	yrot+=yspeed;
	if (xrot>360.0f) xrot-=360.0f;
	if (xrot<0.0f) xrot+=360.0f;
	if (yrot>360.0f) yrot-=360.0f;
	if (yrot<0.0f) yrot+=360.0f;

	/* LAST PASS:	Do The Logos! */	
	doLogo();

	return true;										// Keep Going
}

/* The main drawing function. */
void DrawGLScene()
{
	if (wireframe)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);    

	// Here is the lesson stuff
	if (bumps) {
		if (useMultitexture && maxTexelUnits>1)
			doMesh2TexelUnits();	
		else doMesh1TexelUnits();	
	}
	else doMeshNoBumps();
	// lesson stuff ends here

	CalculateFrameRate();

	glFlush();
	glutSwapBuffers();
}

//------------------------------------------------------------------------
// If rotation angle is greater of 360 or lesser than -360,
// resets it back to zero.
//------------------------------------------------------------------------
void clamp(float *v)
{
	int i;

	for (i = 0; i < 3; i ++)
		if (v[i] > 360 || v[i] < -360)
			v[i] = 0;
}

//------------------------------------------------------------------------
// Moves the screen based on mouse pressed button
//------------------------------------------------------------------------
void glutMotion(int x, int y)
{
	if (mButton == BUTTON_LEFT)
	{
		/* rotates screen */
		rot[0] -= (mOldY - y);
		rot[1] -= (mOldX - x);
		clamp (rot);
	}
	else if (mButton == BUTTON_RIGHT)
	{
		/*
		   translate the screen, z axis
		   gives the idea of zooming in and out 
		   */        
		eye[2] -= (mOldY - y) * 0.05f; // here I multiply by a 0.2 factor to 
		// slow down the zoom
		clamp (rot);
	} 
	else if (mButton == BUTTON_LEFT_TRANSLATE)
	{
		eye[0] += (mOldX - x) * 0.01f;
		eye[1] -= (mOldY - y) * 0.01f;
		clamp (rot);
	}

	mOldX = x;
	mOldY = y;
}

//------------------------------------------------------------------------
// Function that handles mouse input
//------------------------------------------------------------------------
void glutMouse(int button, int state, int x, int y)
{
	if(state == GLUT_DOWN)
	{
		mOldX = x;
		mOldY = y;
		switch(button)
		{
			case GLUT_LEFT_BUTTON:
				if (glutGetModifiers() == GLUT_ACTIVE_CTRL)
				{
					mButton = BUTTON_LEFT_TRANSLATE;
					break;
				} else
				{
					mButton = BUTTON_LEFT;
					break;
				}
			case GLUT_RIGHT_BUTTON:
				mButton = BUTTON_RIGHT;
				break;
		}
	} else if (state == GLUT_UP)
		mButton = -1;
}

/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) 
{
	/* avoid thrashing this procedure */
	usleep(100);

	switch(key)
	{
		case ESCAPE:    /* If escape is pressed, kill everything. */
			{ 
				/* shut down our window */
				glutDestroyWindow(window); 

				/* exit the program...normal termination. */
				exit(0);                   
			}
			break;
		case 'W':
		case 'w':	
			{		
				wireframe = !wireframe;
			}
			break;
		case 'e':
		case 'E':
			{
				emboss = !emboss;
			}
			break;
		case 'm':
		case 'M':
			{
				useMultitexture = ((!useMultitexture) && multitextureSupported);
			}
			break;
		case 'b':
		case 'B':
			{
				bumps = !bumps;
			}
			break;
		case 'f':
		case 'F':
			{
				filter++;
				filter %= 3;
			}
			break;
		default:
			break;

	}
}

void specialKeyPressed(int key, int x, int y)
{
	usleep(100);
	switch(key)
	{
		case GLUT_KEY_PAGE_DOWN:
			z -= 0.02f;
			break;
		case GLUT_KEY_PAGE_UP:
			z += 0.02f;
			break;
		case GLUT_KEY_UP:
			xspeed -= 0.01f;
			break;
		case GLUT_KEY_DOWN:
			xspeed += 0.01f;
			break;
		case GLUT_KEY_RIGHT:
			yspeed += 0.01f;
			break;
		case GLUT_KEY_LEFT:
			yspeed -= 0.01f;
			break;
		default:
			break;
	}
}

int main(int argc, char **argv) 
{  
	/* Initialize GLUT state - glut will take any command line arguments that pertain to it or 
	   X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */  
	glutInit(&argc, argv);  

	/* Select type of Display mode:   
	   Double buffer 
	   RGBA color
	   Alpha components supported 
	   Depth buffer */  
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  

	/* get a 640 x 480 window */
	glutInitWindowSize(640, 480);  

	/* the window starts at the upper left corner of the screen */
	glutInitWindowPosition(0, 0);  

	/* Open a window */  
	window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99");  

	/* Register the function to do all our OpenGL drawing. */
	glutDisplayFunc(&DrawGLScene);  

	/* Go fullscreen.  This is as soon as possible. */
	glutFullScreen();

	/* Even if there are no events, redraw our gl scene. */
	glutIdleFunc(&DrawGLScene);

	/* Register the function called when our window is resized. */
	glutReshapeFunc(&ReSizeGLScene);

	/* Register the function called when the keyboard is pressed. */
	glutKeyboardFunc(&keyPressed);
	/* called when a special key is pressed like SHIFT */
	glutSpecialFunc(specialKeyPressed);

	glutMouseFunc(glutMouse);          // called when the application receives a input from the mouse
	glutMotionFunc(glutMotion);        // called when the mouse moves over the screen with one of this button pressed

	/* Initialize our window. */
	InitGL(640, 480);

	/* Start Event Processing Engine */  
	glutMainLoop();  

	return 1;
}

